package com.group.project.hrms.utils;

import com.group.project.hrms.entity.HolidayRecord;
import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.*;

/**
 * 用于日期的计算和格式化
 */
@Slf4j
public class DateUtil {

    /**
     * 传入格式为“2022-04-10T17: 00: 00.714Z”的String，转化为格式为“yyyy-MM-dd HH:mm:ss”的时间对象
     * @param str
     * @return
     * @throws ParseException
     */
    public Date formatDateByString(String str) throws ParseException {
        //System.out.println("转换前的日期为 " + str);
        String str1 = str.replaceAll(" ", "");//先去掉所有空格
        if (str1.contains(".")) { //去除.之后的所有字符
            int index = str1.indexOf(".");
            str1 = str1.substring(0, index);
        }
        String str2 = str1.replaceAll("T", " ");
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(format.parse(str2));
        calendar.add(Calendar.HOUR_OF_DAY, 8); //由于前端使用了Antd组件，而该时间组件使用了momentjs，在向后台传值时会将时间转换为UTC时间，故在此加8小时
        //System.out.println("转换后的日期为 " + calendar.getTime());
        return calendar.getTime();
    }

    /**
     * 传入字符串 "yyyy-MM-dd HH:mm:ss" ，返回转换后的日期
     * @param str
     * @return
     * @throws ParseException
     */
    public Date formatStringToDate(String str){
        try{
            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            return format.parse(str);
        }catch (ParseException pe){
            pe.printStackTrace();
        }
        return new Date();
    }


    /**
     * 传入两个时间，计算其时间差，单位为小时，精确至小数点后一位
     * @param afterDate
     * @param beforeDate
     * @return
     */
    public Float getTimeDifference(Date afterDate, Date beforeDate){
        float f = (((afterDate.getTime() - beforeDate.getTime()) / 1000f) / 60) / 60;
        BigDecimal b = new BigDecimal(f);
        return b.setScale(1, BigDecimal.ROUND_HALF_UP).floatValue();
    }

    /**
     * 传入两个时间，计算其时间差(除去天数的差异，仅计算两个时间的时钟差)，单位为小时，精确至小数点后一位
     * @param afterDate
     * @param beforeDate
     * @return
     */
    public Float getTimeDifferenceByTime(Date afterDate, Date beforeDate){
        float diffHours = afterDate.getTime() - beforeDate.getTime();
        float f = ((((diffHours / 1000f) / 60) / 60) % 24) == 0 ? (((diffHours / 1000f) / 60) / 60) : ((((diffHours / 1000f) / 60) / 60) % 24) ;
        BigDecimal b = new BigDecimal(f);
        return b.setScale(3, BigDecimal.ROUND_HALF_UP).floatValue(); //ROUND_HALF_UP
    }

    /**
     * 传入一个日期，将该日期向前或向后推i天
     * @param date
     * @return
     */
    public Date dateAddOne(Date date, int i){
        Calendar calendar = new GregorianCalendar();
        calendar.setTime(date);
        calendar.add(calendar.DATE,i); //把日期往后增加一天,整数  往后推,负数往前移动
        date=calendar.getTime(); //这个时间就是日期往后推一天的结果
        return date;
    }


    /**
     * 将多位小数的Float转为1位小数
     * @param f
     * @return
     */
    public float getFloatWithOneBit(float f){
        BigDecimal b = new BigDecimal(f);
        return b.setScale(1, BigDecimal.ROUND_DOWN).floatValue();
    }

    /**
     * 将多位小数的Float转为2位小数
     * @param f
     * @return
     */
    public float getFloatWithTwoBit(float f){
        BigDecimal b = new BigDecimal(f);
        return b.setScale(2, BigDecimal.ROUND_DOWN).floatValue();
    }

    /**
     * 传入月份字符串，返回该月份1号的Calendar对象
     * @param month
     * @return
     */
    public Calendar getCalendarByMonth(String month) {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Calendar calendar = Calendar.getInstance();
        try{
            calendar.setTime(format.parse(month + "-01 00:00:00"));
        }catch (Exception e){
            e.printStackTrace();
        }
        return calendar;
    }

    /**
     * 传入月份字符串，返回该月份最后一天的Date对象
     * @param month
     * @return
     */
    public Date getLastDayCalendarByMonth(String month) {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = null;
        try{
            Date firstDate = format.parse(month + "-01 00:00:00");
            int maxDay = this.getMaxDayMonth(firstDate);
            date = format.parse(month + "-" + maxDay + " 23:59:59");
        }catch (Exception e){
            e.printStackTrace();
        }
        return date;
    }

    /**
     * 传入月份字符串，返回该月份第一天的Date对象
     * @param month
     * @return
     */
    public Date getFirstDayCalendarByMonth(String month) {
        Date date = null;
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        try{
            date = format.parse(month + "-01 00:00:00");
        }catch (Exception e){
            e.printStackTrace();
        }
        return date;
    }

    /**
     * 传入一个日期，获取该日期的Calendar实例
     * @param date
     * @return
     */
    public Calendar getCalendarByDate(Date date){
        Calendar calendar = new GregorianCalendar();
        calendar.setTime(date);
        return calendar;
    }


    /**
     * 将传入的日期date按照指定日期转为一个新Date
     * @param pointDate 指定日期，格式为 2022-05-01
     * @param date 需要转换的日期
     * @return
     */
    public Date getDateByPointDate(String pointDate, Date date){
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Calendar calendar = new GregorianCalendar();
        calendar.setTime(date);
        int hour = calendar.get(Calendar.HOUR_OF_DAY);
        int mins = calendar.get(Calendar.MINUTE);
        String targetDate = pointDate + " " + (hour < 10 ? "0" + hour : hour) + ":" + (mins < 10 ? "0" + mins : mins) + ":00";
        try{
            return format.parse(targetDate);
        }catch (ParseException pe){
            pe.printStackTrace();
        }
        return date;
    }


    /**
     * 传入指定日期的Calendar，获取该日期下的周末日期List
     * @param calendar
     * @return
     */
    public List<Integer> getWeekendListByMonth(Calendar calendar){
        List<Integer> dateList = new ArrayList<>();
        calendar.set(Calendar.DAY_OF_MONTH, 1);// 设置为当月第一天
        int daySize = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);// 当月最大天数
        for (int i = 0; i < daySize; i++) {
            int week = calendar.get(Calendar.DAY_OF_WEEK);
            if (week == Calendar.SATURDAY ) {// 1代表周日，7代表周六 判断这是一个星期的第几天从而判断是否是周末
                dateList.add(i + 1);
            }
            if (week == Calendar.SUNDAY) {// 1代表周日，7代表周六 判断这是一个星期的第几天从而判断是否是周末
                dateList.add(i + 1);
            }
            calendar.add(Calendar.DATE, 1);//在第一天的基础上加1
        }
        return dateList;
    }


    /**
     * 判断一个日期是否为当月的周末
     * @param weekendList 当月的周末日期集合
     * @param i 需要判断的日期 - 具体某一天
     * @return
     */
    public boolean isWeekend(List<Integer> weekendList, int i){
        for (Integer day : weekendList
             ) {
            if(day == i){
                return true;
            }
        }
        return false;
    }


    /**
     * 传入当月节日记录列表和某一天，返回这一天是否为节假日、调休日或者找不到
     * @param holidayRecords
     * @param i
     * @return 1表示假日，2表示调休，0表示没找到
     */
    public int checkHoliday(List<HolidayRecord> holidayRecords, int i){
        for (HolidayRecord hr : holidayRecords
             ) {
            if(hr.getAppointDate().getDate() == i){ //休假类型，1表示假日，2表示工作日调休，3表示周末调休
                return hr.getHolidayType();
            }
        }
        return 0; //表示没找到
    }


    /**
     * 传入一个日期字符串，获取这一天是周几
     * @param date
     * @return
     */
    public int getWeekNumber(String date){
        SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd");
        try{
            Date d = f.parse(date);
            Calendar cal = Calendar.getInstance();
            cal.setTime(d);
            //因为数组下标从0开始，而返回的是数组的内容，是数组{1,2,3,4,5,6,7}中用1~7来表示，所以要减1
            int week = cal.get(Calendar.DAY_OF_WEEK) - 1;
            if (week == 0) {
                week = 7;
            }
            return week;
        }catch (Exception e){
            e.printStackTrace();
        }
        return 0;
    }


    /**
     * 判断一个字符串日期，是否包含在一个日期数组中
     * @param date
     * @param dateList
     * @return
     */
    public boolean isContactDate(String date, List<Date> dateList){
        SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd");
        try{
            Date targetDate = f.parse(date);
            for (Date sourceDate : dateList
                 ) {
                if(f.format(targetDate).equals(f.format(sourceDate))){
                    return true;
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return false;
    }


    /**
     * 传入一个日期，返回2022-04-05格式的String
     * @param date
     * @return
     */
    public String dateToString(Date date){
        Calendar calendar = new GregorianCalendar();
        calendar.setTime(date);
        return calendar.get(Calendar.YEAR) + "-" + ((calendar.get(Calendar.MONTH) + 1 < 10 ? "0" + (calendar.get(Calendar.MONTH) + 1) : calendar.get(Calendar.MONTH) + 1)) + "-" + (calendar.get(Calendar.DATE) < 10 ? "0" + calendar.get(Calendar.DATE)  : calendar.get(Calendar.DATE));
    }

    /**
     * 传入一个日期，返回2022-04-05 09:00:00 格式的String
     * @param date
     * @return
     */
    public String dateToStringByFormat(Date date){
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Calendar calendar = new GregorianCalendar();
        calendar.setTime(date);
        String dateString = "";
        try{
            dateString = format.format(date);
        }catch (Exception e){
            e.printStackTrace();
        }
        return dateString;
    }

    /**
     * 传入一个日期，返回这个日期是几号
     * @param date
     * @return
     */
    public int getDayByDate(Date date){
        Calendar calendar = new GregorianCalendar();
        calendar.setTime(date);
        return calendar.get(Calendar.DATE);
    }


    /**
     * 传入一个日期，返回这个日期是哪个月
     * @param date
     * @return
     */
    public int getMonthByDate(Date date){
        Calendar calendar = new GregorianCalendar();
        calendar.setTime(date);
        return calendar.get(Calendar.MONTH) + 1;
    }

    /**
     * 传入一个日期，返回这个日期是哪一年
     * @param date
     * @return
     */
    public int getYearByDate(Date date){
        Calendar calendar = new GregorianCalendar();
        calendar.setTime(date);
        return calendar.get(Calendar.YEAR);
    }

    /**
     * 传入一个日期，获取该日期所在月份一共有几天
     * @param date
     * @return
     */
    public int getMaxDayMonth(Date date){
        Calendar calendar = new GregorianCalendar();
        calendar.setTime(date);
        return calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
    }

    /**
     * 判断两个时间的间隔的天数
     * @param date1
     * @param date2
     * @return
     */
    public int getDifferentDaysByMillisecond(Date date1, Date date2){
        Calendar cal1 = Calendar.getInstance();
        cal1.setTime(date1);

        Calendar cal2 = Calendar.getInstance();
        cal2.setTime(date2);
        int day1= cal1.get(Calendar.DAY_OF_YEAR);
        int day2 = cal2.get(Calendar.DAY_OF_YEAR);

        int year1 = cal1.get(Calendar.YEAR);
        int year2 = cal2.get(Calendar.YEAR);
        if(year1 != year2) {//同一年
            int timeDistance = 0 ;
            for(int i = year1 ; i < year2 ; i ++)
            {
                if(i%4==0 && i%100!=0 || i%400==0) //闰年
                {
                    timeDistance += 366;
                }
                else //不是闰年
                {
                    timeDistance += 365;
                }
            }

            return timeDistance + (day2-day1) ;
        } else {//不同年
            return day2-day1;
        }
    }


    /**
     * 判断两个日期是否是同一天
     * @param date1
     * @param date2
     * @return
     */
    public boolean isSameDay(Date date1, Date date2){
        LocalDate localDate1 = date1.toInstant()
                .atZone(ZoneId.systemDefault())
                .toLocalDate();
        LocalDate localDate2 = date2.toInstant()
                .atZone(ZoneId.systemDefault())
                .toLocalDate();
        return localDate1.isEqual(localDate2);
    }


    /**
     * 传入一个月的任意一天的String, 例如：2022-01-02 00：00：00 返回这个月最后一天的Date对象
     * @param dateStr
     * @return
     */
    public Date getLastDateByString(String dateStr){
        Date date = this.formatStringToDate(dateStr);
        int max = this.getMaxDayMonth(date);
        int year = this.getYearByDate(date);
        int month = this.getMonthByDate(date);
        String lastDay = year + "-" + (month < 10 ? "0" + month : month) + "-" + max + " 23:59:59";
        return this.formatStringToDate(lastDay);
    }


    /**
     * 传入毫秒数，返回小时数，精确至小数点后1位
     * @param mills
     * @return
     */
    public float getHoursByMills(long mills){
        return this.getFloatWithOneBit(((float) ( mills / 1000 )) / 3600) ;
    }


    /**
     * 传入日期，获取该日期的小时数
     * @param date
     * @return
     */
    public int getHourByDate(Date date){
        Calendar calendar = this.getCalendarByDate(date);
        return calendar.get(Calendar.HOUR_OF_DAY);
    }


    /**
     * 传入两个日期，计算工龄
     * @param nowTime 目前的时间
     * @param workTime 入职的时间
     * @return
     */
    public int getWorkAge(Date nowTime, Date workTime){
        int year = 0;
        //当前时间的年月日
        Calendar cal = Calendar.getInstance();
        cal.setTime(nowTime);
        int nowYear = cal.get(Calendar.YEAR);
        int nowMonth = cal.get(Calendar.MONTH);
        int nowDay = cal.get(Calendar.DAY_OF_MONTH);

        //开始工作时间的年月日
        cal.setTime(workTime);
        int workYear = cal.get(Calendar.YEAR);
        int workMonth = cal.get(Calendar.MONTH);
        int workDay = cal.get(Calendar.DAY_OF_MONTH);

        //得到工龄
        year = nowYear - workYear; //得到年差
        //若目前月数少于开始工作时间的月数，年差-1
        if (nowMonth < workMonth){
            year  = year - 1;
        }else if (nowMonth == workMonth){
            //当月数相等时，判断日数，若当月的日数小于开始工作时间的日数，年差-1
            if (nowDay < workDay){
                year = year - 1;
            }
        }

        return year;
    }


    /**
     * 获取传入日期的下一个月的String 格式 2022-09
     * @param date
     * @return
     * @throws Exception
     */
    public String getNextMonthStrByDate(Date date) throws Exception {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.add(cal.MONTH, 1);
        SimpleDateFormat dft = new SimpleDateFormat("yyyy-MM");
        return dft.format(cal.getTime());
    }


    /**
     * 获取传入日期的String 格式 2022-09
     * @param date
     * @return
     * @throws Exception
     */
    public String getMonthStrByDate(Date date) throws Exception {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        SimpleDateFormat dft = new SimpleDateFormat("yyyy-MM");
        return dft.format(cal.getTime());
    }



}
